#ifndef _YINFAB_H_
#define _YINFAB_H_

#include <iostream>
#include <cmath>
#include <ctime>
#include "REAL.H"
#include "Box.H"
#include "Vector.H"
#include "RealVect.H"
#include "FArrayBox.H"
#include "BoxIterator.H"
#include "BoxLayoutData.H"
#include "DisjointBoxLayout.H"
#include "LevelData.H"
#include "DataIterator.H"
#include "IntVectSet.H"
#include "LayoutIterator.H"
#include "UsingNamespace.H"

#include "NamespaceHeader.H"

#include "YinSet.h"
#include "SegmentedRealizableSpadjor.h"
#include <algorithm>

template <int Order>
class YinCell
{
  enum
  {
    Dim = 2
  };

protected:
  YinSet<Dim, Order> *Pys_ = NULL;
  int index_ = 0;
  int InterfaceCordNum_ = -1;

public:
  YinCell(){};
  ~YinCell(){};
  YinCell(const int &_index) { index_ = _index; };
  YinCell(YinSet<Dim, Order> *_Pys, const int &_index)
  {
    Pys_ = _Pys;
    index_ = _index;
  };
  void setYinSet(YinSet<Dim, Order> *_Pys) { Pys_ = _Pys; };
  void setIndex(int _index) { index_ = _index; };
  void setInterfaceCordNum(int _IntergaceCordNum) { InterfaceCordNum_ = _IntergaceCordNum; };

  const auto &getYinSet() const { return Pys_; };
  const auto &getIndex() const { return index_; };
  const auto &getInterfaceCordNum() const { return InterfaceCordNum_; };
};

template <int Order>
class YinFab : public BaseFab<YinCell<Order>>
{
  enum
  {
    Dim = 2
  };
  enum Type
  {
    Full = 2,
    NotTrivial = 1,
    Empty = 0
  };

protected:
  YinSet<Dim, Order> *Pys_ = NULL;
  Vec<Real, 2> origin_;
  Real dx_ = 1.0;
  Real tol_ = 1e-8;
  std::vector<YinSet<Dim, Order>> InterfaceYinSets;
  std::vector<Chombo::IntVect> InterfaceIndexs;

public:
  YinFab();
  YinFab(const Box &a_box, int a_ncomp = 1, YinCell<Order> *a_alias = NULL);
  void setOrigin(Vec<Real, 2> _origin) { origin_ = _origin; };
  void setDx(Real _dx) { dx_ = _dx; };
  void setTol(Real _tol) { tol_ = _tol; };
  void setYinSet(YinSet<Dim, Order> *_Pys) { Pys_ = _Pys; };
  const auto &getYinSet() const { return Pys_; };

  const YinSet<Dim, Order> createRectYinSet_(const Vec<Real, 2> &lo, const Vec<Real, 2> &hi)
  {
    Curve<2, Order> res = createRect<Order>(lo, hi);
    SegmentedRealizableSpadjor<Order> bdr({res});
    YinSet<2, Order> ys(bdr, 0);
    return ys;
  };

  bool is_intersect_(const YinSet<2, Order> &_ys1, const YinSet<2, Order> &_ys2, Real _tol)
  {
    auto srs12 = meet(_ys1, _ys2, _tol);
    if (srs12.getBoundaryCycles().size() != 0)
      return true;
    return false;
  }

  void dumpAll(std::string _bufName) const
  {
    using namespace Chombo;
    Box b1 = (*this).box();
    BoxIterator bit(b1);
    for (bit.begin(); bit.ok(); ++bit)
    {
      IntVect iv = bit();
      for (int i = 0; i < 1; i++)
      {
        std::string bufB = _bufName;
        bufB.append(std::to_string(iv[0]));
        bufB.append("by");
        bufB.append(std::to_string(iv[1]));
        bufB.append(".dat");
        char file_name[30];
        std::strcpy(file_name, bufB.c_str());
        std::ofstream output(file_name, std::ios::binary);
        (*this).dump(output, iv);
      }
    }
    std::cout << "Complete!" << std::endl;
  }

  ///输入Box中的坐标，如果是接触边界就保存;
  void dump(std::ostream &os, Chombo::IntVect _CordIndex) const
  {
    const int index = (*this)(_CordIndex, 0).getIndex();
    if (index == 1)
    {
      (InterfaceYinSets[(*this)(_CordIndex, 0).getInterfaceCordNum()]).dump(os);
      std::cout << "The cordIndex " << _CordIndex << " is not trivial. Saved." << std::endl;
    }
    else
    {
      std::cout << "The cordIndex " << _CordIndex << " is ";
      if (index == 0)
      {
        std::cout << "Empty." << std::endl;
      }
      else
      {
        std::cout << "Full." << std::endl;
      };
    };
  }

  void generateYinFab()
  {
    InterfaceIndexs.clear();
    InterfaceYinSets.clear();
    using namespace Chombo;
    Box b = this->box();
    BoxIterator bit(b);
    const int NumComponents = this->nComp();
    int InterfaceCounter = -1;
    for (bit.begin(); bit.ok(); ++bit)
    {
      IntVect iv = bit();
      for (int i = 0; i < NumComponents; i++)
      {
        YinCell<Dim> ycTemp;
        Vec<Real, Dim> lo = {dx_ * iv[0], dx_ * iv[1]};
        lo = lo + origin_;
        Vec<Real, Dim> hi = {lo[0] + dx_, lo[1] + dx_};
        // std::cout << lo << hi;
        YinSet<Dim, Order> rectys = createRectYinSet_(lo, hi);
        if (is_intersect_(rectys, *Pys_, tol_))
        {
          YinSet<Dim, Order> ysIntersect = intersect(rectys, *Pys_, tol_);
          if (rectys.equal(ysIntersect, tol_))
          {
            ycTemp.setIndex(Full);
            // std::cout << "Full" << std::endl;
          }
          else
          {
            InterfaceCounter++;
            InterfaceIndexs.push_back(iv);
            InterfaceYinSets.push_back(ysIntersect);
            ycTemp.setYinSet(&(InterfaceYinSets[InterfaceYinSets.size() - 1]));
            ycTemp.setInterfaceCordNum(InterfaceCounter);
            ycTemp.setIndex(NotTrivial);
            // std::cout << "NotTrivial" << std::endl;
          };
        }
        else
        {
          ycTemp.setIndex(Empty);
          // std::cout << "Empty" << std::endl;
        };
        (*this)(iv, i).setYinSet(ycTemp.getYinSet());
        (*this)(iv, i).setIndex(ycTemp.getIndex());
        (*this)(iv, i).setInterfaceCordNum(ycTemp.getInterfaceCordNum());
      }
    }
  };
};

template <int Order>
YinFab<Order>::YinFab(const Box &a_box,
                      int a_n,
                      YinCell<Order> *a_alias)
    : BaseFab<YinCell<Order>>(a_box, a_n, a_alias)
{
}

#include "NamespaceFooter.H"
#endif //do nothing
